﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace Zsea.HTML
{
    public class Document
    {
        // Fields
        private List<string> _defaultSingleNode;

        internal Dictionary<string, Node> pathCollection = new Dictionary<string, Node>();

        // Methods
        public Document()
        {
            List<string> list = new List<string>();
            list.Add("input");
            list.Add("br");
            list.Add("hr");
            list.Add("img");
            list.Add("meta");
            list.Add("link");
            this._defaultSingleNode = list;
        }

        public Node ElementById(string id)
        {
            return this.DocumentNode._AllNode.Find(delegate(Node o)
            {
                return o.Attributes.Exists(p => (p.Name.ToUpper() == "ID") && (p.Value == id));
            });
        }

        public void Load(string html)
        {
            Predicate<string> predicate = null;
            this.pathCollection = new Dictionary<string, Node>();
            this.DocumentNode = new Node();
            this.DocumentNode.NodeType = NodeType.Document;
            this.Text = html;
            Operator start = Operator.Start;
            Stack<Node> stack = new Stack<Node>();
            Node item = null;
            Node cNode = null;
            Attribute attribute = null;
            for (int i = 0; i < html.Length; i++)
            {
                string str;
                char ch = html[i];
                switch (ch)
                {
                    case '<':
                        if (((start != Operator.Text) && (start != Operator.Start)) && (start != Operator.TagEnd))
                        {
                            goto Label_0564;
                        }
                        str = string.Empty;
                        try
                        {
                            str = html.Substring(i, 9);
                        }
                        catch
                        {
                        }
                        if (!(str.ToUpper() == "<!DOCTYPE"))
                        {
                            goto Label_0187;
                        }
                        if ((cNode != null) && (cNode.NodeType == NodeType.Tag))
                        {
                            stack.Push(cNode);
                        }
                        if ((cNode != null) && (cNode.NodeType == NodeType.Text))
                        {
                            if (stack.Count > 0)
                            {
                                item = stack.Pop();
                                item.Children.Add(cNode);
                                stack.Push(item);
                            }
                            else
                            {
                                this.DocumentNode.Children.Add(cNode);
                            }
                        }
                        cNode = new Node();
                        cNode.NodeType = NodeType.Comment;
                        start = Operator.DoctypeLeft;
                        goto Label_06E3;

                    case '=':
                        if (start != Operator.AttributeName)
                        {
                            goto Label_0564;
                        }
                        start = Operator.AttributeEq;
                        goto Label_06E3;

                    case '>':
                        if (((start != Operator.TagSpace) && (start != Operator.TagName)) && (start != Operator.AttributeNameEnd))
                        {
                            goto Label_04F2;
                        }
                        start = Operator.TagEnd;
                        goto Label_06E3;

                    case '-':
                        {
                            string str2 = string.Empty;
                            if (start != Operator.CommentLeft)
                            {
                                goto Label_0564;
                            }
                            try
                            {
                                str2 = html.Substring(i - 3, 4);
                            }
                            catch
                            {
                            }
                            if (!(str2 == "<!--"))
                            {
                                goto Label_0564;
                            }
                            start = Operator.CommentText;
                            goto Label_06E3;
                        }
                    case ' ':
                        if (start != Operator.AttributeName)
                        {
                            goto Label_040E;
                        }
                        start = Operator.TagSpace;
                        goto Label_06E3;

                    case '!':
                        goto Label_0564;

                    case '"':
                        if (start != Operator.AttributeEq)
                        {
                            goto Label_04A2;
                        }
                        start = Operator.AttributeDoubleValue;
                        goto Label_06E3;

                    case '\'':
                        switch (start)
                        {
                            case Operator.AttributeEq:
                                start = Operator.AttributeSingleValue;
                                goto Label_06E3;

                            case Operator.AttributeSingleValue:
                                cNode.Attributes.Add(attribute);
                                attribute = null;
                                start = Operator.TagSpace;
                                goto Label_06E3;

                            case Operator.DoctypeLeft:
                                start = Operator.DoctypeSingle;
                                goto Label_0564;

                            case Operator.DoctypeSingle:
                                start = Operator.DoctypeLeft;
                                break;
                        }
                        goto Label_0564;
                }
                goto Label_0564;
            Label_0187:
                try
                {
                    str = html.Substring(i, 4);
                }
                catch
                {
                }
                if (str == "<!--")
                {
                    if ((cNode != null) && (cNode.NodeType == NodeType.Tag))
                    {
                        stack.Push(cNode);
                    }
                    if ((cNode != null) && (cNode.NodeType == NodeType.Text))
                    {
                        if (stack.Count > 0)
                        {
                            item = stack.Pop();
                            item.Children.Add(cNode);
                            stack.Push(item);
                        }
                        else
                        {
                            this.DocumentNode.Children.Add(cNode);
                        }
                    }
                    cNode = new Node();
                    cNode.NodeType = NodeType.Comment;
                    start = Operator.CommentLeft;
                }
                else
                {
                    char ch2 = '\0';
                    try
                    {
                        ch2 = html.Substring(i, 2)[1];
                    }
                    catch
                    {
                    }
                    if (((ch2 >= 'a') && (ch2 <= 'z')) || ((ch2 >= 'A') && (ch2 <= 'Z')))
                    {
                        if ((cNode != null) && (cNode.NodeType == NodeType.Tag))
                        {
                            stack.Push(cNode);
                        }
                        if ((cNode != null) && (cNode.NodeType == NodeType.Text))
                        {
                            if (stack.Count > 0)
                            {
                                item = stack.Pop();
                                item.Children.Add(cNode);
                                stack.Push(item);
                            }
                            else
                            {
                                this.DocumentNode.Children.Add(cNode);
                            }
                        }
                        cNode = new Node();
                        cNode.NodeType = NodeType.Tag;
                        start = Operator.TagLeft;
                    }
                    else
                    {
                        if (ch2 != '/')
                        {
                            goto Label_0564;
                        }
                        if ((cNode != null) && (cNode.NodeType == NodeType.Tag))
                        {
                            stack.Push(cNode);
                        }
                        if ((cNode != null) && (cNode.NodeType == NodeType.Text))
                        {
                            if (stack.Count > 0)
                            {
                                item = stack.Pop();
                                item.Children.Add(cNode);
                                stack.Push(item);
                            }
                            else
                            {
                                this.DocumentNode.Children.Add(cNode);
                            }
                        }
                        cNode = new Node();
                        cNode.NodeType = NodeType.Tag;
                        start = Operator.TagLeft;
                    }
                }
                goto Label_06E3;
            Label_040E:
                switch (start)
                {
                    case Operator.AttributeEq:
                        goto Label_06E3;

                    case Operator.AttributeNoFlagValue:
                        cNode.Attributes.Add(attribute);
                        attribute = null;
                        start = Operator.TagSpace;
                        goto Label_06E3;

                    default:
                        if (start != Operator.TagSpace)
                        {
                            if (start != Operator.TagName)
                            {
                                goto Label_0564;
                            }
                            start = Operator.TagSpace;
                        }
                        goto Label_06E3;
                }
            Label_04A2:
                if (start == Operator.AttributeDoubleValue)
                {
                    cNode.Attributes.Add(attribute);
                    attribute = null;
                    start = Operator.TagSpace;
                    goto Label_06E3;
                }
                if (start == Operator.DoctypeLeft)
                {
                    start = Operator.DoctypeDouble;
                }
                else if (start == Operator.DoctypeDouble)
                {
                    start = Operator.DoctypeLeft;
                }
                goto Label_0564;
            Label_04F2:
                if (start == Operator.CommentText)
                {
                    string str3 = string.Empty;
                    try
                    {
                        str3 = html.Substring(i - 2, 3);
                    }
                    catch
                    {
                    }
                    if (str3 == "-->")
                    {
                        start = Operator.TagEnd;
                        goto Label_06E3;
                    }
                }
                if ((start == Operator.AttributeName) || (start == Operator.AttributeNoFlagValue))
                {
                    start = Operator.TagEnd;
                    cNode.Attributes.Add(attribute);
                    attribute = null;
                    goto Label_06E3;
                }
                if (start == Operator.DoctypeLeft)
                {
                    start = Operator.TagEnd;
                    goto Label_06E3;
                }
            Label_0564:
                if ((((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'))) || (ch == '/'))
                {
                    switch (start)
                    {
                        case Operator.TagLeft:
                        case Operator.TagName:
                            cNode._name = cNode._name + ch;
                            start = Operator.TagName;
                            goto Label_06E3;

                        case Operator.TagSpace:
                            if (attribute != null)
                            {
                                cNode.Attributes.Add(attribute);
                                attribute = null;
                            }
                            start = Operator.AttributeName;
                            attribute = new Attribute();
                            attribute.Name = attribute.Name + ch;
                            goto Label_06E3;
                    }
                }
                if ((ch == '/') && (start == Operator.AttributeName))
                {
                    start = Operator.AttributeName;
                    attribute = new Attribute();
                    attribute.Name = attribute.Name + ch;
                }
                else if (start == Operator.AttributeName)
                {
                    attribute.Name = attribute.Name + ch;
                }
                else if (start == Operator.AttributeEq)
                {
                    attribute.Value = attribute.Value + ch;
                    start = Operator.AttributeNoFlagValue;
                }
                else if (((start == Operator.AttributeDoubleValue) || (start == Operator.AttributeNoFlagValue)) || (start == Operator.AttributeSingleValue))
                {
                    attribute.Value = attribute.Value + ch;
                }
                else
                {
                    if ((start == Operator.TagEnd) || (start == Operator.Start))
                    {
                        if ((cNode != null) && (cNode.NodeType == NodeType.Tag))
                        {
                            stack.Push(cNode);
                        }
                        cNode = new Node();
                        cNode.NodeType = NodeType.Text;
                        start = Operator.Text;
                    }
                    if ((start == Operator.DoctypeLeft) || (start == Operator.DoctypeDouble))
                    {
                    }
                }
            Label_06E3:
                cNode.startFlag = cNode.startFlag + ch;
                if (start == Operator.TagEnd)
                {
                    if (cNode.NodeType == NodeType.Comment)
                    {
                        if (stack.Count > 0)
                        {
                            Node node2 = stack.Pop();
                            node2.Children.Add(cNode);
                            stack.Push(node2);
                        }
                        else
                        {
                            this.DocumentNode.Children.Add(cNode);
                        }
                    }
                    else if (cNode.NodeType == NodeType.Tag)
                    {
                        if (cNode._name.ToUpper() == "P")
                        {
                            item = null;
                            if (stack.Count > 0)
                            {
                                item = stack.Pop();
                            }
                            if (item != null)
                            {
                                if (item._name.ToUpper() == "P")
                                {
                                    item.endFlag = "</p>";
                                }
                                else
                                {
                                    stack.Push(item);
                                }
                            }
                        }
                        Match match = Regex.Match(cNode.startFlag, @"</\s{0,}([a-zA-Z]+?)\s{0,}>", RegexOptions.IgnoreCase);
                        if (match.Success)
                        {
                            string name = match.Groups[1].Value;
                            if (Array.Exists<Node>(stack.ToArray(), delegate(Node o)
                            {
                                return string.Equals(o.Name, name, StringComparison.CurrentCultureIgnoreCase);
                            }))
                            {
                                item = stack.Pop();
                                while (item != null)
                                {
                                    if (item._name.ToUpper() == name.ToUpper())
                                    {
                                        item.endFlag = cNode.startFlag;
                                        cNode = null;
                                        if (stack.Count > 0)
                                        {
                                            Node node3 = stack.Pop();
                                            node3.Children.Add(item);
                                            stack.Push(node3);
                                        }
                                        else
                                        {
                                            this.DocumentNode.Children.Add(item);
                                        }
                                        break;
                                    }
                                    item.endFlag = "</" + item._name + ">";
                                    if (stack.Count > 0)
                                    {
                                        Node node4 = stack.Pop();
                                        node4.Children.Add(item);
                                        stack.Push(node4);
                                    }
                                    else
                                    {
                                        this.DocumentNode.Children.Add(cNode);
                                    }
                                    if (stack.Count <= 0)
                                    {
                                        break;
                                    }
                                    item = stack.Pop();
                                }
                            }
                            else if (stack.Count > 0)
                            {
                                item = stack.Pop();
                                item.Children.Add(cNode);
                                stack.Push(item);
                            }
                            else
                            {
                                this.DocumentNode.Children.Add(cNode);
                            }
                        }
                        else if (Regex.Match(cNode.startFlag, @"/\s{0,}>", RegexOptions.IgnoreCase).Success)
                        {
                            if (stack.Count > 0)
                            {
                                Node node5 = stack.Pop();
                                node5.Children.Add(cNode);
                                stack.Push(node5);
                            }
                            else
                            {
                                this.DocumentNode.Children.Add(cNode);
                            }
                            cNode = null;
                        }
                        else
                        {
                            if (predicate == null)
                            {
                                predicate = delegate(string o)
                                {
                                    return o.ToUpper() == cNode._name.ToUpper();
                                };
                            }
                            if (this._defaultSingleNode.Exists(predicate))
                            {
                                if (stack.Count > 0)
                                {
                                    Node node6 = stack.Pop();
                                    node6.Children.Add(cNode);
                                    stack.Push(node6);
                                }
                                else
                                {
                                    this.DocumentNode.Children.Add(cNode);
                                }
                                cNode = null;
                            }
                        }
                    }
                }
            }
            while (stack.Count > 0)
            {
                Node node7 = stack.Pop();
                if (stack.Count > 0)
                {
                    Node node8 = stack.Pop();
                    node8.Children.Add(node7);
                    stack.Push(node8);
                }
                else
                {
                    this.DocumentNode.Children.Add(node7);
                }
            }
            this.DocumentNode.SetXPath(string.Empty, this);
        }

        public Node SelectNode(string xPath)
        {
            Node node = null;
            try
            {
                node = this.pathCollection[xPath.ToUpper()];
            }
            catch
            {
            }
            return node;
        }

        // Properties
        public Node DocumentNode
        {

            get;
            private set;
        }

        public string Text
        {

            get;

            private set;
        }

        // Nested Types
        private enum Operator
        {
            Start,
            Text,
            TagLeft,
            DoctypeLeft,
            DoctypeDouble,
            DoctypeSingle,
            CommentLeft,
            CommentText,
            TagName,
            TagSpace,
            AttributeName,
            AttributeNameEnd,
            AttributeEq,
            AttributeNoFlagValue,
            AttributeSingleValue,
            AttributeDoubleValue,
            TagEnd
        }
    }





}
